/*
 * Copyright (c) 2017-2021, Lindenis Tech. Ltd.
 * All rights reserved.
 *
 * File:
 *
 * Description:
 *
 * Author:
 *      xiaoshujun@lindeni.com
 *
 * Create Date:
 *      2021/04/01
 *
 * History:
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>

#ifdef MELIS_OS
#include <finsh.h>
#endif

#include "osal_types.h"
#include "osal_mutex.h"
#include "osal_common.h"

#include "lindtunnel.h"
#include "ldt_error.h"
#include "ldt_ioctrl_def.h"

#define LOG_TAG "sample_ldt_dev"
#include "osal_log.h"

#define LISENCE_KEY "TEST"

typedef struct _sample_ldt_dev_ctx_t
{
    int                     chn_running;
    pthread_t               chn_tid;
    lock_mutex              mutex;
} sample_ldt_dev_ctx_t;

typedef struct _sample_thread_param_t
{
    sample_ldt_dev_ctx_t *  p_ctx;
    int                     sid;
    int                     gid;
} sample_thread_param_t;

static int on_video_start(sample_ldt_dev_ctx_t * p_ctx, int32_t sid, int32_t gid, uint8_t * data)
{
    int ret = 0;
    ldt_ioctrl_video_start_resp_t resq = {0};
    resq.result = 0;
    ret = LDT_Dev_SendIOCtrl(sid, gid, LDT_IOCTRL_IPC_VIDEO_START_RESP,
                            (int8_t *)&resq, sizeof(resq));
    if (ret < 0) {
        loge("LDT_IOCTRL_IPC_VIDEO_START_RESP failed, ret: %d", ret);
        return LDTER_FAILED;
    }

    logd("now send video frame");

    /*
     * test
     */
    int i = 0;
    uint8_t * frame = (uint8_t *)malloc(3*1024*1024);
    int frame_size = read_file("/mnt/UDISK/single_frame.h264", frame, 3*1024*1024);
    if (frame_size <= 0)
    {
        free(frame);
        loge("read test video file failed");
        return LDTER_FAILED;
    }
    logd("read test video file ok, size: %d", frame_size);
    dump_hex(frame, 64, 16);
    for (i = 0; i < 10; i++)
    {
        LDT_VFrame_t format;
        format.codec_id     = LDT_CODEC_VIDEO_H264;
        format.format       = LDT_FRAME_FORMAT_ES_I;
        format.timestamp    = 0;
        ret = LDT_Dev_SendVideo(sid, gid, &format, frame, frame_size);
        if (ret < 0)
        {
            loge("LDT_Dev_SendVideo failed, ret: %d", ret);
        }
        else
        {
            logv("LDT_Dev_SendVideo ok");
        }
        usleep(500000);
    }
    free(frame);

    logd("send video frame end");

    return LDTER_OK;
}

static int on_video_stop(sample_ldt_dev_ctx_t * p_ctx, int32_t sid, int32_t gid, uint8_t * data)
{
    int ret = 0;
    ldt_ioctrl_video_stop_resp_t resq = {0};
    resq.result = 0;
    ret = LDT_Dev_SendIOCtrl(sid, gid, LDT_IOCTRL_IPC_VIDEO_STOP_RESP,
                            (int8_t *)&resq, sizeof(resq));
    if (ret < 0) {
        loge("LDT_IOCTRL_IPC_VIDEO_STOP_RESP failed, ret: %d", ret);
        return LDTER_FAILED;
    }

    logd("on_video_stop");

    return LDTER_OK;
}

static int32_t on_get_video_fmt(sample_ldt_dev_ctx_t * p_ctx, int32_t sid, int32_t gid,
                                    const uint8_t * data)
{
    int ret = LDTER_FAILED;

    ldt_ioctrl_get_vfmt_resp_t resp = {0};
    resp.codec      = LDT_CODEC_VIDEO_H264;
    resp.quality    = LDT_VIDEO_QUALITY_AUTO;
    resp.width      = 0;
    resp.height     = 0;
    ret = LDT_Dev_SendIOCtrl(sid, gid, LDT_IOCTRL_IPC_GET_VIDEO_FMT_RESP,
                            (int8_t *)&resp, sizeof(resp));
    if (ret < 0) {
        loge("LDT_IOCTRL_IPC_GET_VIDEO_FMT_RESP failed, ret: %d", ret);
        return LDTER_FAILED;
    }

    return LDTER_OK;
}

static int handler_channel_ioctrl(sample_ldt_dev_ctx_t * p_ctx, int32_t sid, int32_t gid,
                                    int32_t cmd, uint8_t * data)
{
    int ret = 0;
    logd("Handle cmd: 0x%lx", cmd);
    switch(cmd)
    {
        case LDT_IOCTRL_IPC_VIDEO_START_REQ:
        {
            return on_video_start(p_ctx, sid, gid, data);
        }
        case LDT_IOCTRL_IPC_VIDEO_STOP_REQ:
        {
            return on_video_stop(p_ctx, sid, gid, data);
        }
        case LDT_IOCTRL_IPC_GET_VIDEO_FMT_REQ:
        {
            return on_get_video_fmt(p_ctx, sid, gid, data);
        }
        default:
            logw("unknown cmd: 0x%lx", cmd);
            break;
    }

    return 0;
}

static void * thread_channel_ioctrl(void *param)
{
    sample_thread_param_t * p_param = (sample_thread_param_t *)param;
    sample_ldt_dev_ctx_t * p_ctx = p_param->p_ctx;
    int sid = p_param->sid;
    int gid = p_param->gid;
    free(param);
    int ret = 0;
    int cmd = 0;
    uint8_t data[LDT_PKG_MAX_SIZE];
    int32_t size = 0;

    logd("thread_channel_ioctrl running ...");

    while(p_ctx->chn_running)
    {
        size = LDT_PKG_MAX_SIZE;
        ret = LDT_Dev_RecvIOCtrl(sid, gid, &cmd, (uint8_t *)&data, &size, 1000);
        if (ret < 0)
        {
            continue;
        }

        handler_channel_ioctrl(p_ctx, sid, gid, cmd, (uint8_t *)&data);
    }

    logd("thread_channel_ioctrl exit");

    return 0;
}

static int32_t sample_ldt_dev_on_event(void * user, LDT_MSG_e msg, void * ext1, void * ext2)
{
    sample_ldt_dev_ctx_t * p_ctx = user;
    int ret = 0;

    switch (msg)
    {
        case LDT_MSG_LOGIN:
            logi("login success");
            break;
        case LDT_MSG_LOGOUT:
            logi("logout, something wrong ...");
            break;
        case LDT_MSG_SESSION_CONNECT:
            logi("client connect in, sid: %d", *(int*)ext1);
            break;
        case LDT_MSG_SESSION_DISCONN:
            logi("client disconnect, sid: %d", *(int*)ext1);
            break;
        case LDT_MSG_CHANNEL_GROUP_CREATED:
        {
            logi("new channel group sid: %d, gid: %d", *(int*)ext1, *(int*)ext2);
            p_ctx->chn_running = 1;
            sample_thread_param_t * param = (sample_thread_param_t *)malloc(sizeof(sample_thread_param_t));
            param->p_ctx = p_ctx;
            param->sid = *(int*)ext1;
            param->gid = *(int*)ext2;
            ret = pthread_create(&p_ctx->chn_tid, NULL, thread_channel_ioctrl, (void *)param);
            if (ret) {
                loge("create thread_ldt_dev_login failed: %s", strerror(errno));
                return LDTER_FAILED;
            }
            break;
        }
        case LDT_MSG_CHANNEL_GROUP_DESTORY:
        {
            logi("channel group destory sid: %d, gid: %d", *(int*)ext1, *(int*)ext2);
            p_ctx->chn_running = 0;
            if (p_ctx->chn_tid)
            {
                pthread_join(p_ctx->chn_tid, 0);
                p_ctx->chn_tid = 0;
            }
            break;
        }
        default:
            break;
    }

    return 0;
}

static int sample_ldt_dev_process(const char * uid, const char * password)
{
    sample_ldt_dev_ctx_t contex = {0};
    sample_ldt_dev_ctx_t * p_ctx = &contex;
    int ret = 0;

    init_lock(&p_ctx->mutex);

    LDT_Cfg_t config = {0};
    config.on_event = sample_ldt_dev_on_event;
    config.user = p_ctx;
    config.max_session = 4;
    config.timeout_ms = 5000;
    ret = LDT_Dev_Initialize(&config, LISENCE_KEY, uid, password);

    while(1)
    {
#ifdef MELIS_OS
        usleep(100000);
#else
        signed char key_val;
        ret = wait_input_char(&key_val, 100000);
        if (ret < 0)
        {
            break;
        }
        else if (ret == 0)
        {
            continue;
        }

        if (key_val > 0 && key_val)
        {
            if (key_val != '\n')
            {
                printf("********* key_val: %c ********** \n", key_val);
            }

            if (key_val == 'q' || key_val == 'Q')
            {
                break;
            }
        }
#endif
    }

    destroy_lock(&p_ctx->mutex);
    LDT_Dev_Cleanup();

    return 0;
}

static void _sample_ldt_dev_usage(char * this)
{
    printf("\033[32m");
    printf("Usage: \n");
    printf("    %s -u [uid] -p [password] \n", this);
    printf("        -u [uid]        : the uid of this device, defaut: '0001' \n");
    printf("        -p [password]   : the password of this device, defaut: '666666' \n");
    printf("                        : 'ldt_client' can connect this device by 'uid' & 'password' \n");
    printf("    such as: %s -u 0001 -p 666666 \n", this);
    printf("\033[0m\n");
}

#ifdef MELIS_OS
static int main_melis(int argc, char **argv)
#else
int main(int argc, char **argv)
#endif
{
    int ret = 0;
    int i = 0;
    char * uid = "0001";
    char * password = "666666";

    if ((argc == 2)
        && (!strcmp(argv[1], "-h")))
    {
        _sample_ldt_dev_usage(argv[0]);
        return 0;
    }

    for (i = 1; i < argc; )
    {
        if (!strcmp(argv[i], "-u"))
        {
            uid = argv[i+1];
            i += 2;
        }
        else if (!strcmp(argv[i], "-p"))
        {
            password = argv[i+1];
            i += 2;
        }
        else
        {
            i++;
        }
    }

    sample_ldt_dev_process(uid, password);

    return 0;
}

#ifdef MELIS_OS
FINSH_FUNCTION_EXPORT_ALIAS(main_melis, __cmd_sample_ldt_dev, lindmpp sample);
#endif
